package security

import (
	"context"
	"github.com/cloudwego/hertz/pkg/app"
	"github.com/cloudwego/hertz/pkg/protocol/consts"
	"github.com/ezcloud/ezapp/pkg/core/log"
	"github.com/ezcloud/ezapp/pkg/core/views"
	"github.com/ezcloud/ezapp/pkg/middleware/ctxcache"
	"regexp"
	"strings"
	"time"
)

var security_key struct{}

const HeaderAuthorizationKey = "Authorization"

func parseBearerAuthToken(authHeader string) string {
	if len(authHeader) == 0 {
		return ""
	}
	parts := strings.Split(authHeader, "Bearer")
	if len(parts) != 2 {
		return ""
	}

	token := strings.TrimSpace(parts[1])
	if len(token) == 0 {
		return ""
	}

	return token
}

type Session struct {
	UserID    string
	CreatedAt time.Time
	ExpiresAt time.Time
}

type AuthFilter struct {
	excludeUrls []*regexp.Regexp
}

func (f *AuthFilter) isExcludeUrls(c *app.RequestContext) bool {
	isE := false

	uriPath := c.URI().Path()

	for _, rule := range f.excludeUrls {
		if rule.MatchString(string(uriPath)) {
			isE = true
			break
		}
	}

	return isE
}

func (f *AuthFilter) ServeHTTP(ctx context.Context, c *app.RequestContext) {
	r := f.isExcludeUrls(c)
	if r {
		c.Next(ctx)
		return
	}

	if len(c.Request.Header.Get(HeaderAuthorizationKey)) == 0 {
		log.CtxErrorf(ctx, "missing authorization in header")
		c.AbortWithStatusJSON(consts.StatusUnauthorized, views.NewFailedC[string](views.BAD_TOKEN, ""))
		return
	}

	token := parseBearerAuthToken(c.Request.Header.Get(HeaderAuthorizationKey))
	if len(token) == 0 {
		log.CtxErrorf(ctx, "missing authorization in header")
		c.AbortWithStatusJSON(consts.StatusUnauthorized, views.NewFailedC[string](views.BAD_TOKEN, ""))
		return
	}

	// 检查 token，换为 session
	userId, ok := openauthService(ctx, token)
	if !ok {
		log.CtxErrorf(ctx, "openauth authorization token failed")
		c.AbortWithStatusJSON(consts.StatusUnauthorized, views.NewFailedC[string](views.BAD_TOKEN, ""))
		return
	}

	ctxcache.Store(ctx, security_key, &Session{UserID: userId})
	log.Info("session: %s", userId)
	c.Next(ctx)
}

func SecurityHandler(ex []string) app.HandlerFunc {
	af := &AuthFilter{excludeUrls: make([]*regexp.Regexp, 0)}
	for _, e := range ex {
		af.excludeUrls = append(af.excludeUrls, regexp.MustCompile(e))
	}
	return af.ServeHTTP
}

func openauthService(ctx context.Context, token string) (string, bool) {
	return "admin", true
}
